import {
  CadScene,
  ConstraintType,
  CSDashLine,
  CSPoint,
  CSS2DObject,
  CSys,
  CurveStyle,
  EventsRes,
  ICurve,
  KEYBOARD_KEY,
  MathTool,
  MouseInfo,
  SceneGraph,
  SceneSetup,
  SketchFrame,
} from '@amcax/base';
import { ctx2 } from '@app-cad/module';
import { Picker2D, PickerMgr } from '@amcax/renderer';
import {
  dashOptions,
  isEffectiveValue,
  isEquals,
  lineWidthOptions,
  renderSketch,
  updataRenderDim,
} from '@app-cad/common/utils';
import { wsApi } from '@app-cad/server';
import { Object3D, Shape, Vector2tuple, Vector3 } from 'three-legacy';
import { sketchEventCenter } from '../events/SketchEventCenter';
import { GroupsType } from './sketch/cmd-render-pick/type';
import {
  snapManager,
  SnapResult,
} from '@app-base/common/utils/sketchTools/SnapManager';

export const guideColor = '#1683e8';
export type ConstraintsType<T> = {
  curve: Vector2tuple | string;
  target: Vector2tuple | Vector2tuple;
  type: T;
  value?: number;
  points: Vector2tuple[];
};

export enum PickType {
  Null,
  Point,
  Line,
}

export type ArcParams = {
  x: number;
  y: number;
  radius: number;
  startAngle: number;
  endAngle: number;
  clockWise: boolean;
};

export type Setting = {
  dashSize?: number;
  gapSize?: number;
  dashed?: boolean;
  lineWidth?: number;
  color?: string;
};
type PreviewKey = 'line' | 'aline' | 'rline' | 'yline' | 'xline';
export default abstract class CmdBase {
  private static readonly SEMI_CIRCLE_SEGMENTS = 32;
  private static readonly ARC_SEGMENT = 32;

  pickedCurve: ICurve & { groups?: GroupsType[] }; /* 拾取的线信息 */
  unPickedIds: string[] = []; /* 不拾取的线id */
  assisGroup: Object3D;
  faceCSys: CSys; /* 面坐标系 */
  sketchId: string; /* 草图id */
  sketchFrame: SketchFrame; // 草图坐标系
  assistPoints: CSPoint[][] = [];
  mouseWV: Vector3; // 鼠标在草图坐标系中的坐标
  isGuide = true; // 是否有引导线
  isSnap = true; // 是否吸附
  closeRightClick: boolean = true; // 是否关闭右键
  css2dLabel: CSS2DObject; // css2d标签
  isVorH: 'V' | 'H' | null = null; // 竖直还是水平
  protected picker = PickerMgr.getPicker<Picker2D>('2d');
  protected pickType: PickType = null; // 点击类型（点、线）
  protected pickSnapInfo: ICurve = null; // 吸附的信息
  protected trackingData = {};
  private cadScene: CadScene; // cad场景
  private sceneSetup: SceneSetup; // 场景设置
  private planeNormal: Vector3;
  private planeOrigin: Vector3;
  private snapAssistPoint: CSPoint;
  // 预览线
  previewLines: { [key in PreviewKey]?: CSDashLine } = {};

  currentSnapInfo: SnapResult;

  constructor() {
    this.init();
  }

  protected _constraint = null;

  // 设置约束
  set constraint(value) {
    this._constraint = value;
  }

  // 子类需要实现的方法
  abstract registerEvents();

  abstract unregisterEvents();

  abstract reset();

  abstract releaseObjects();

  // 鼠标按下事件
  enterKeyDown: () => void = () => {};

  // 初始化
  init() {
    this.cadScene = ctx2.cadScene as CadScene;
    this.sceneSetup = ctx2.viewer.sceneSetup;
    this.assisGroup = this.cadScene.assisGroup;
    const { frame, sketchId } = sketchEventCenter().getSketchInfo();
    this.sketchId = sketchId;
    if (frame) {
      this.faceCSys = MathTool.createCsysFromPlacement(frame);
      this.planeNormal = this.faceCSys.getNormalVc3();
      this.planeOrigin = this.faceCSys.getOriginVc3();
      this.sketchFrame = new SketchFrame(frame);
    }
  }

  // 获取线的类型 (虚线、实线)
  get lineType() {
    return sketchEventCenter().getLineType();
  }

  // 销毁
  destroy() {
    // 移除约束
    this._constraint = null;
    this.assisGroup.remove(this.css2dLabel);
    this.unregisterEvents();
    this.releaseObjects();
    this.reset();
    SceneGraph.emptyGroup(this.assisGroup);
  }

  // 添加退出事件
  addExit(exitFunc: () => void) {
    this.exitFunc = exitFunc;
  }

  // 创建约束
  createConstraint = async (
    curveId: string,
    targetId: string,
    constraint: number,
    val: number = 0,
  ) => {
    const result: ReturnDataInfo = await wsApi.createSketchConstraint(
      this.sketchId,
      curveId,
      targetId,
      constraint,
      val,
    );
    this.render(result);
  };
  protected exitFunc = () => {};
  protected calcRadius = (start: Vector3, end: Vector3): number => {
    return start.clone().distanceTo(end.clone());
  };

  // 创建更新标签
  creatUpdateLabel(position: Vector3, label?: string) {
    const p = position.clone();
    p.x = p.x + 16 / ctx2.getCameraZoom();
    p.y = p.y + 20 / ctx2.getCameraZoom();
    const pos = this.sketchFrame.toWorld(p);
    if (this.css2dLabel) {
      this.css2dLabel.position.set(pos.x, pos.y, pos.z);
      if (label) {
        this.css2dLabel.element.children[0].innerHTML = `<span class="text_p">${label}</span>`;
      }
      this.showLabel(true);
    } else {
      const divHtml = document.createElement('div');
      divHtml.className = 'draw_Input_container';
      divHtml.innerHTML = `<span class="text_p">${label}</span>`;
      this.css2dLabel = new CSS2DObject(divHtml);
      this.css2dLabel.position.set(pos.x, pos.y, pos.z);
      this.css2dLabel.name = 'css2dInputLable';
      this.assisGroup.add(this.css2dLabel);
    }
  }

  // 显示隐藏标签
  showLabel(flag: boolean) {
    if (this.css2dLabel) {
      this.css2dLabel.visible = flag;
    }
  }

  // 判断两个点x或y是否相等
  isSamePoint(p1: Vector3, p2: Vector3): 'H' | 'V' | null {
    if (p1.x === p2.x) {
      return 'V';
    } else if (p1.y === p2.y) {
      return 'H';
    } else {
      return null;
    }
  }

  // 创建圆弧
  getArcVertexs(params: ArcParams) {
    const shape = new Shape().absarc(
      params.x,
      params.y,
      params.radius,
      params.startAngle,
      params.endAngle,
      params.clockWise,
    );
    const points = shape.getPoints(25);
    if (points.length < 51) {
      return [];
    }
    return points.map((item) => new Vector3(item.x, item.y, 0));
  }

  createSemiCircle(
    center: Vector3,
    radius: number,
    direction: Vector3,
  ): Vector3[] {
    const segments = CmdBase.SEMI_CIRCLE_SEGMENTS;
    const points: Vector3[] = [];
    const startAngle = Math.atan2(direction.y, direction.x);

    for (let i = 0; i <= segments; i++) {
      const angle = startAngle + (Math.PI * i) / segments;
      const x = center.x + radius * Math.cos(angle);
      const y = center.y + radius * Math.sin(angle);
      points.push(new Vector3(x, y, 0));
    }

    return points;
  }

  createArc(
    center: Vector3,
    startPoint: Vector3,
    endPoint: Vector3,
    arcRadius: number,
    arcClockwise: boolean,
  ) {
    const segments = CmdBase.ARC_SEGMENT;
    const points: Vector3[] = [];

    const startAngle = Math.atan2(
      startPoint.y - center.y,
      startPoint.x - center.x,
    );
    const endAngle = Math.atan2(endPoint.y - center.y, endPoint.x - center.x);
    let totalAngle = endAngle - startAngle;

    if (arcClockwise && totalAngle > 0) {
      totalAngle -= 2 * Math.PI;
    } else if (!arcClockwise && totalAngle < 0) {
      totalAngle += 2 * Math.PI;
    }

    const angleStep = totalAngle / segments;
    for (let i = 0; i <= segments; i++) {
      const angle = startAngle + angleStep * i;
      const x = center.x + arcRadius * Math.cos(angle);
      const y = center.y + arcRadius * Math.sin(angle);
      points.push(new Vector3(x, y, 0));
    }

    return points;
  }

  // 创建线
  createUpdateLine(
    vertexs: Vector3[],
    key: PreviewKey = 'line', // 预览线的key
    lineType?: CurveStyle, // 线的类型
    setting?: Setting, // 线的设置
    assisGroup?: Object3D,
  ) {
    const _vertexs = [];
    vertexs?.forEach((item) => {
      if (isEffectiveValue(item)) {
        _vertexs.push(this.sketchFrame.toWorld(item));
      }
    });
    if (_vertexs?.length < 2) {
      return;
    }
    const dashed =
      (lineType || this.lineType) === CurveStyle.SolidLine ? false : true;
    // CSLine的一个bug，vertex长度改变时，无法正确更新
    if (this.previewLines[key]?.getVertexs()?.length === _vertexs.length) {
      this.previewLines[key].update({
        vertexs: _vertexs,
        dashed,
      });
    } else {
      this.previewLines[key]?.parent?.remove(this.previewLines[key]);
      this.previewLines[key]?.destroy();
      let param = {
        vertexs: _vertexs,
        lineWidth: dashed ? lineWidthOptions.dashed : lineWidthOptions.soild,
        dashed,
        depthTest: false, // 不进行深度测试
        ...dashOptions,
      };
      if (!['line', 'aline'].includes(key)) {
        param = Object.assign(param, setting);
      }
      this.previewLines[key] = new CSDashLine({
        ...param,
      });
      this.previewLines[key].addTo(assisGroup || this.assisGroup);
    }
  }

  // 清除线
  clearLine(keys?: string[]) {
    if (Array.isArray(keys) && keys.length > 0) {
      for (let i = 0; i < keys.length; i++) {
        const key = keys[i];
        if (this.previewLines[key]) {
          this.previewLines[key].destroy();
          this.previewLines[key] = null;
        }
      }
    } else {
      for (const key in this.previewLines) {
        this.previewLines[key]?.destroy();
        this.previewLines[key] = null;
      }
    }
  }

  // 删除辅助点
  protected removeAssistPoint(assistPoint?: CSPoint) {
    this.assisGroup.remove(assistPoint || this.snapAssistPoint);
  }

  // 鼠标按下事件
  protected onKeyUp = (e: EventsRes<KeyboardEvent>) => {
    if (e.key === KEYBOARD_KEY.ESC) {
      this.exitFunc?.();
    } else if (e.key === KEYBOARD_KEY.ENTER) {
      this.enterKeyDown();
    }
  };

  // 根据鼠标位置获取点位置（世界坐标）
  protected getPointByMouse(mouse: MouseInfo) {
    const mousePoint = this.sceneSetup.mouseOnPlane(
      mouse.x,
      mouse.y,
      this.planeNormal,
      this.planeOrigin,
    );
    return this.sketchFrame.toLocal(mousePoint);
  }

  // 获取拾取的信息
  protected getPickLine(ev: EventsRes): ICurve {
    const info = this.picker.pick(ev, null, null, this.unPickedIds) as ICurve;
    return this.pickedCurve?.id === info?.id ||
      this.pickedCurve?.relative?.includes(info?.id) ||
      info?.relative?.includes(info?.id)
      ? null
      : info;
  }

  // 设置 mouseWV
  /**
   * @param ev 鼠标事件
   * @param isSnap 是否吸附 （吸附草图线段）
   * */
  protected setMouseWV(
    ev: EventsRes,
    isSnap = true,
    isGuide = true,
    needDraw = true,
  ) {
    const snapResult = snapManager().snap(ev as EventsRes<PointerEvent>, {
      needExtend: isGuide,
      needPick: isSnap,
      needDraw,
    });

    this.currentSnapInfo = snapResult;
    this.mouseWV =
      snapResult == null
        ? this.getPointByMouse(ev.mouse)
        : new Vector3(...snapResult.point);
    if (!isSnap) {
      this.pickSnapInfo = this.picker.pick(ev) ?? snapResult?.info?.[0]?.curve;
    } else {
      this.pickSnapInfo = snapResult?.info?.[0]?.curve;
    }

    if (this.pickSnapInfo == null) {
      this.pickType = PickType.Null;
    } else if (this.pickSnapInfo?.type === 'Point') {
      this.pickType = PickType.Point;
    } else {
      this.pickType = PickType.Line;
    }
  }

  getPointConstraintsFromSnapResult(
    snapResult: SnapResult,
    point?: Vector2tuple,
    includesPoint = false,
  ) {
    return (snapResult?.info ?? [])
      .filter(
        (c) => c.curve != null && (includesPoint || c.curve.type !== 'Point'),
      )
      .map((c) => ({
        curve: c.curve.id,
        type:
          c.curve.type === 'Point'
            ? ConstraintType.Coincident
            : ConstraintType.PointOnObject,
        target: point ?? snapResult.point,
      }));
  }

  // 添加辅助点
  protected createAssistPoint(
    point: Vector3,
    isMove: boolean = false,
    index: number = 0,
    isAddTempPoint: boolean = false, // 是否添加临时辅助点 (用于显示引导线 -> 竖直和水平辅助线)
  ) {
    if (!isEffectiveValue(point)) {
      return null;
    }
    if (!Array.isArray(this.assistPoints[index])) {
      this.assistPoints[index] = [];
    }
    const assistPoints = this.assistPoints[index];
    isMove = isMove ?? false;
    const assistPoint: CSPoint = new CSPoint({
      vertex: this.sketchFrame.toWorld(point),
      pointType: 'midPoint',
    });
    assistPoint.hoverOn();
    assistPoint.setSize(8);
    assistPoint.renderOrder = 99;

    if (!isMove || assistPoints.length == 1) {
      assistPoints.push(assistPoint);
      this.assisGroup.add(assistPoint);
    } else {
      this.assisGroup.remove(assistPoints[assistPoints.length - 1]);
      this.assisGroup.add(assistPoint);
      assistPoints[assistPoints.length - 1] = assistPoint;
      ctx2.viewer.requestRender();
    }
    if (!isMove && isAddTempPoint) {
      snapManager().addTempSnapPoints(point);
    }
    return assistPoint;
  }

  /**
   * @param pA
   * @param pB
   * @param pC
   * */
  getPointPosition(pA: Vector3, pB: Vector3, pC: Vector3) {
    // 线段的方向向量
    const direction = new Vector3().subVectors(pB, pA);
    // 从点A到点C的向量
    const vectorAC = new Vector3().subVectors(pC, pA);
    // 假设一个垂直于线段的向量（例如，使用Z轴向量）
    const normal = new Vector3(0, 0, 1);
    // 计算点积
    const dot = vectorAC.dot(normal.crossVectors(direction, normal));
    if (dot > 0) {
      // 'up'
      return false;
    } else if (dot < 0) {
      // 'down'
      return true;
    } else {
      //  'on
      return true;
    }
  }

  // 渲染
  protected async render(data?: ReturnDataInfo, isResetDim: boolean = false) {
    return await renderSketch(this.sketchId, data).finally(() => {
      this.unPickedIds = [];
      if (isResetDim) {
        updataRenderDim(this.sketchId);
      }
    });
  }

  // 获取约束
  getConstraint(snapInfo: SnapType, p: Vector3, hasPoint = false) {
    const { curveId, pickType, point } = snapInfo;
    const type = ConstraintType.PointOnObject;
    const target = [p.x, p.y];
    if (!isEquals(point, p)) {
      return null;
    }
    if (pickType === PickType.Point && hasPoint) {
      return {
        curve: curveId,
        target,
        type: ConstraintType.Coincident,
      };
    } else if (pickType === PickType.Line) {
      return {
        curve: curveId,
        target,
        type,
      };
    }
    return null;
  }

  removeTempPoint() {
    snapManager().clearTempSnapPoint();
  }
}

export type ReturnDataInfo = {
  added?: string[];
  removed?: string[];
  changed?: string[];
};
export type SnapType = {
  curveId: string;
  pickType: PickType;
  point?: Vector3;
};
